package com.effectiv.gooruda.rest.ext;

import java.io.IOException;
import java.util.Locale;

import javax.annotation.Priority;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.Provider;

import jodd.cache.LRUCache;
import jodd.util.StringUtil;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.effectiv.gooruda.domain.Application;
import com.effectiv.gooruda.repository.ApplicationRepository;

@Component
@Provider
@Slf4j
@Priority(2)
public class SecurityAndAuditingFilter implements ContainerRequestFilter,
		ContainerResponseFilter {

	private LRUCache<String, Application> cache = new LRUCache<String, Application>(
			100);

	@Autowired
	private ApplicationRepository repository;
	
	@Autowired
	private InvocationAuditProcessor auditProcessor;

	public Application getApplication(String publicKey) {
		// get application in cache first
		Application app = cache.get(publicKey);
		if (app == null) {
			// Fallback on DB
			log.debug("LOoking in DB because of cache miss.");
			app = repository.findByPublicKey(publicKey);
			log.debug("app = {}", app);
		}
		
		return app;
	}
	
	public void abortProcessing(int errorCode, String errorMessage, ContainerRequestContext context){
		ResponseBuilder responseBuilder = Response
				.status(Status.UNAUTHORIZED);

		ExtResponse extResponse = new ExtResponse();
		extResponse.setErrorCode(errorCode);
		extResponse
				.setMessage(errorMessage);

		responseBuilder.entity(extResponse);

		context.abortWith(responseBuilder.build());
	}

	public void filter(ContainerRequestContext context) throws IOException {
		String signature = context.getHeaders().getFirst("signature");
		String publicKey = context.getHeaders().getFirst("public-key");

		log.debug("signature = {}", signature);
		log.debug("publicKey = {}", publicKey);

		String uri = context.getUriInfo().getAbsolutePath().toString();
		log.debug("getAbsolutePath = {}", uri);

		if (StringUtil.indexOfIgnoreCase(uri, "apidocs") != -1) {
			return;
		}
		
		Application app = getApplication(publicKey);
		if (app == null) {
			abortProcessing(5000, "No application found for the given public key. Unauthorized request.", context);
		
		} else {
			cache.put(publicKey, app);

			// form the signature with salt
			String hash = getHash(app);

			// match the signature
			if (!StringUtil.equals(signature, hash)) {
				
				abortProcessing(5001, "Signature mismatch. Unauthorized request.", context);
			}
		}

		// put app in threadlocal
		SecurityContextHolder.set(app);
	}

	private String getHash(Application app) {
		String secret = app.getPrivateKey();
		String salt = app.getSalt();

		return PasswordEncoder.encode(secret, salt);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * javax.ws.rs.container.ContainerResponseFilter#filter(javax.ws.rs.container
	 * .ContainerRequestContext, javax.ws.rs.container.ContainerResponseContext)
	 */
	public void filter(ContainerRequestContext reqCtx,
			ContainerResponseContext resCtx) throws IOException {

		log.debug("Cleaning up request");
		
		String api = getApi(reqCtx.getUriInfo().getPath());
		String remoteHost = (String) reqCtx.getProperty("RESTEASY_REMOTE_HOST");
		String locale = ((Locale) reqCtx.getProperty("RESTEASY_LOCALE")).toString();
		
		log.info("Remote Host ## = {}", remoteHost);
		log.info("locale ## = {}", locale);
		
		auditProcessor.logRequest(reqCtx, resCtx, SecurityContextHolder.get(), api, remoteHost, locale);
		
		
		SecurityContextHolder.remove();

	}
	
	private String getApi(String path){
		String api = "";
		if(path.indexOf("file")!=-1){
			api = "file";
		}
		else{
			
		}
		
		return api;
	}
}
